﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace LogFileParser
{
    public class LogFileParser
    {
        public delegate void Logger(string msg);
        public delegate LogFileItem Parser(LogFileItem parent,string pline);
        public delegate bool IncludeItem(LogFileItem lfi);
        private Logger _logger = null;
        private Parser _parser;
        private IncludeItem _includeItem = null;
        private long _totalLines = 0;
        private long _includedLines = 0;
        private long _excludedLines = 0;
        private LogFileItem _parent = null;
        private List<LogFileItem> items = new List<LogFileItem>();

        public LogFileParser(string logfilename) : this(logfilename,null,null)
        {
        }

        public LogFileParser(string logfilename, Logger logger,IncludeItem includeitem)
        {
            _logger = logger;
            _parser = parser;
            if (includeitem == null)
            {
                _includeItem = includeItem;
            }
            else
            {
                _includeItem = includeitem;
            }

            StreamReader sr = null;
            string inline;
            try
            {
                LogFileItem lfi;
                sr = File.OpenText(logfilename);
                while ((inline = sr.ReadLine()) != null)
                {
                    lfi = _parser(_parent,inline);
                    if (_parent == null)
                    {
                        _parent = lfi;
                    }
                    else
                    {
                        if (_includeItem(lfi))
                        {
                            items.Add(lfi);
                            ++this.IncludedLines;
                        }
                        else
                        {
                            ++this.ExcludedLines;
                        }
                    }
                }
            }
            catch (IOException ioexp)
            {
                if (_logger != null)
                {
                    WriteLog(ioexp.Message);
                }
                else
                {
                    throw (new IOException("LogFileParser", ioexp));
                }
            }
            finally
            {
                if (sr != null)
                {
                    sr.Close();
                }
            }
        }

        private LogFileItem parser(LogFileItem parent,string pline)
        {
            string[] tokens;
            char[] delims = { '\t' };
            ++this.TotalLines;
            LogFileItem lfi = null;

            if (TotalLines == 1)     // This is the first line and contains the column header
            {
                lfi = new LogFileItem(); 
                tokens = pline.Split(delims);
                foreach (string token in tokens)
                {
                    string tkn = token.Trim();
                    lfi[tkn] = tkn;
                }
            }
            else
            {
                lfi = new LogFileItem(parent);
                tokens = pline.Split(delims);
                int i = 0;
                foreach (string fieldname in parent.FieldNames)
                {
                    lfi.SetValue(fieldname, tokens[i]);
                    ++i;
                }
            }

            return lfi;
        }

        // This method returns a list of values that all of the items in the named field name
        // have
        public List<string> GetUniqueValues(string name)
        {
            List<string> values = new List<string>();
            foreach (LogFileItem lfi in Items)
            {
                if (!values.Contains(lfi[name]))
                {
                    values.Add(lfi[name]);
                }
            }
            return values;
        }
        delegate bool _test(string x,string y); 

        public List<LogFileItem> Filter(string fieldName, string value, string oper)
        {
            List<LogFileItem> items = new List<LogFileItem>();

            _test op = (x, y) => { return x == y; };

            switch (oper)
            {
                case "=":
                    op = (x,y) => { return x == y; };
                    break;
                case "<>":
                    op = (z, t) => { return z != t; };
                    break;
                case "!=":
                    op = (z, t) => { return z != t; };
                    break;
                //case "<":
                //    op = (z, t) => { return z < t; };
                //    break;
                //case "<=":
                //    op = (z, t) => { return z <= t; };
                //    break;
                //case ">":
                //    op = (z, t) => { return z > t; };
                //    break;
                //case ">=":
                //    op = (z, t) => { return z >= t; };
                //    break;
            }

            foreach (LogFileItem lfi in Items)
            {
                if (op(lfi[fieldName],value))
                {
                    items.Add(lfi);
                }
            }

            return items;
        }

        public List<LogFileItem> Filter(string fieldName, string value)             
        {
            return Filter(fieldName, value, "=");
        }

        private bool includeItem(LogFileItem lfi)
        {
            return true;
        }

        private void WriteLog(string msg)
        {
            if (_logger != null)
            {
                _logger(msg);
            }
        }

        public long TotalLines
        {
            get { return _totalLines; }
            private set { _totalLines = value; }
        }
        public long IncludedLines
        {
            get { return _includedLines; }
            private set { _includedLines = value; }
        }
        public long ExcludedLines
        {
            get { return _excludedLines; }
            private set { _excludedLines = value; }
        }
        public List<string> Fields
        {
            get { return _parent.FieldNames; }
        }

        public List<LogFileItem> Items
        {
            get { return items; }
        }

        public LogFileItem this[int indx]
        {
            get { return Items[indx]; }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            foreach (LogFileItem lfi in items)
            {
                sb.AppendLine(lfi.ToString());
            }
            return sb.ToString();
        }
    }
}
